ECE 5725 Final Project(Wesdnesday session)
Miaocheng Li(ml2743)
Yang Chen(yc2733)
Guitar Hero is a series of music rhythm game video games first released in 2005, in which players use a guitar-shaped game
controller to simulate playing primarily lead, bass guitar, and rhythm guitar across numerous songs. Players match notes that
scroll on-screen to colored fret buttons on the controller, strumming the controller in time to the music in order to score
points, and keep the virtual audience excited. The games attempt to mimic many features of playing a real guitar, including
the use of fast-fingering hammer-ons and pull-offs and the use of the whammy bar to alter the pitch of notes.
With the introduction of Guitar Hero World Tour in 2008, the game includes support for a
four-player band including vocals and drums. The series initially used mostly cover versions of songs created by WaveGroup
Sound, but most recent titles feature soundtracks that are fully master recordings, and in some cases, special re-recordings,
of the songs. Later titles in the series feature support for downloadable content in the form of new songs.
That's been a really successful video game, everything perfect except that they use a fake guitar. So what we want to do,
is a guitar hero game using a real electric guitar.
This project has a main program, then the main program will call the init program, the init program can call the choose program, the choose program can have three options (respectively bent_to_fly, c_major, e_string), choose any one of them will trigger the game program loop execution, but the game will continue to run in a loop. Before triggering, the choose program will first call the data_process program to process the data, in the game loop, it will repeatedly call the detection program and the refresh program, at the same time, the game will also continue to run a listener program pause, when the game's runtime runs out, the game program will call the settlement program
In our implementation of Fast Fourier Transform (FFT), the most critical decision pertains to the selection of window size,
that is, the number of consecutive sampling frames on which the FFT is performed. Firstly, it is commonly understood that the
time complexity of FFT is O(n log n), hence it is preferable to select a window size that is a power of two. Secondly, and more
importantly, the required resolution must be considered. The standard sampling rate for audio streams is 44.1kHz, and the resolution
of the FFT output is determined by dividing the sampling rate by the window size. Essentially, opting for a smaller window size reduces
the waiting time for sufficient sampling frames but compromises resolution. Conversely, a larger window size allows for higher resolution
decomposition of the original signal into frequency-domain components, at the expense of increased mandatory delay.
The question then becomes: what resolution do we need? Modern music is based on the 12-tone equal temperament, with the standard
pitch A4 set at 440 Hz. This standard provides precise frequencies for each musical note and the interval between them. The required
resolution should be fine enough to ensure that adjacent notes are not represented on the same frequency axis, meaning the resolution
should be greater than the smallest frequency interval between notes on a guitar, which is approximately 5 Hz on the sixth string and
over 10 Hz on the others. Therefore, we settled on a window size of 4096 for the FFT, yielding a resolution of 10.7 Hz.
Nonetheless, this entails a compulsory latency of approximately 0.1 second to fill a 4k window buffer before commencing the FFT.
This latency is a critical consideration in applications like guitar hero games, where longer waiting times are undesirable. Although
the resolution is not entirely satisfactory, we compensated by leveraging the harmonics of audio signals. Our post-FFT process involves
calculating the modulus of the frequency domain list, performing an argsort, and checking if the fundamental frequency and its second
harmonic are among the top five most prominent frequencies. This approach aids in better distinguishing semitones that are closely spaced
The decoupling of refreshing and sampling/detection
In the main game loop, there are several tasks, e.g. refreshing, audio stream management (sampling),
detection, updating score. We found that the refreshing of the game interface is kind of different from
the others, because we don't want choppy animation, so the refresh function should be called in each cycle
to redraw as many times as possible in each time unit. But for the rest tasks, they are less frequently executed,
because we have to wait for buffers to be filled in the background. So we decoupled the refreshing and other tasks,
packaging the refreshing function into a class.
While this makes the game loop much clearer, another problem comes out. Since the refreshing function
is decoupled from others, to synchronize them, we have to maintain the “now = time.time() - start_time”
synchronized with each other in these two parts. And this turned to be tricky when we designed the pause-resume
procedure. We will talk about this later.
Using timeline to drive the refreshing and sampling/detection
In previous labs, there were pygame examples “bounce balls” where two balls are moving on the screen. In that example,
what we do is basically adding an increment (velocity) to the locations of two balls in each cycle. This works well for
that because each loop cycle takes almost the same time. But for our project, between each time the “refresh” function
is called, the interval differs due to varying time consumption for audio stream management and audio processing.
Thus we designed several timeline arrays to drive the game loop. In the refresher, a “concern list” storing all the notes
needed to be shown on the screen at certain timeslots is maintained. When the “now” variable turns greater than
generation_time[idx], the note idx would be pushed into the concern list. It would be popped out once the “now” variable
exceeds out_time[idx]. Similarly in the game loop, another concern list is maintained to store all the notes needed
to be sampled and detected.
Another choice is to use a thread for the refresher function. But we didn't choose to do so because refresher and other
procedures in the game loop could suffer from data race if they are running on separate threads.
Only start detection after the buffer is filled
To eliminate the choppy animation, another thing we found important is that, we should only start the fft detection
after the buffer is filled up. Because we call stream.read(a certain size of chunk) before fft detection, the program
would stall to wait for enough data from the buffer, if the buffer hasn't been filled yet. Thus, we always record the
last timeslot when data was read from the buffer, and call the stream.read() function after a time interval which is
greater than the expected time for the buffer to be filled. The expected interval can be calculated according to the
44.1k Hz sampling rate.
The fine-grained overlapped sampling windows
The compulsory latency of 4K sampling frames is around 0.1s. However, if the song is 60 bpm (which is fairly slow),
then the appropriate window during which the note is considered right to the rhythm could be just around 0.2 or 0.3 second.
Thus we may only have 2 or 3 chances to capture the note if we always wait for 4k sampling frames, which makes it
impossible to distinguish the “too early”/”perfect”/”too late” timing. Thus, we used a 1K stride: For the first window,
we wait for a whole of 4K frames. But for the rest, each time we wait for a new of 1K frames, popping out the first 1K
of the previous 4K window, and appending the new 1K. In this way, we gain more chances to capture the note, as well as
distinguishing the timing. Note that a 1K stride is what we tested to be optimal, since a smaller stride could cause
overwhelming latency due to other procedures on the embedded system.
Audio Stream Management
To minimize latency, we use threading to create new audio streams. Except this, a tricky thing found on the rpi is that,
we only have limited physical resources (for maybe, buffers). We cannot create infinite audio streams. In fact, we can at
most create 16 audio streams. Once the number exceeds 16, the recording could be really weird, or there could be runtime error.
But another point is that, we cannot stick to just one stream, since sometimes, one note starts to be sampled before the
sampling and detection for the last note ends, especially when the bpm goes faster. In the end, we used a mod-4 mapping.
Basically, there will be at most 4 audio streams created, and the notes would be mapped to different audio streams (modulo 4).
Thus we won't violate the resource limitation, and also maintain functionality.
The pause and resume
The pause and resume turned out to be the most tricky design in the project. Before we designed the pause-resume function,
we already added a background metronome to the game. Actually, the metronome is super important for audio games. Thus when
resuming to the game, not only the notes should be where they were before pausing, the metronome should be played for some
time so that the notes are still right to the beat rhythm.
The global_var module
There are many modules in this project, which makes it demanding to use global variables. But we found it tricky to use
global variables in python. Especially when the global variable is modified in different levels of modules. We chose to use
a global_var module to manage all the global variables. Basically, there is a dictionary storing all the global variables.
In any other modules, we call global_var.set_value() or global_var.get_value() to interact with global variables. This
eliminates the trouble due to circular imports of global variables. Also, we have special functions to update the detection
results, detection timing, and the score.
real_guitar_hero/
│
├── main.py
├── block_27_shutdown.py
│
├── audio/
│ ├── 55bpm.wav
│ ├── 60bpm.wav
│ ├── fail_game_over.wav
│ └── game_over.wav
│
├── data/
│ ├── Bent_to_fly.csv
│ ├── C_major.csv
│ └── E_String.csv
│
├── image/
│ ├── cover.png
│
└── util/
├── __init__.py
├── choose.py
├── data_process.py
├── detection.py
├── game.py
├── global_var.py
├── init.py
├── pause.py
├── refresh.py
└── settlement.py
Our team developed a novel Guitar Hero software for the Raspberry Pi,
uniquely designed to work with real electric guitars. The core technology underpinning this project is Fast Fourier
Transform (FFT), an algorithm vital for converting complex audio signals into frequency components. This implementation
provided us with numerous insights and learnings.
The software capitalizes on FFT to accurately detect musical notes played on the guitar,
translating them into in-game actions. A significant challenge was balancing FFT's window size
to optimize both latency and frequency resolution. Our choice of a 4096 window size, giving a 10.7 Hz
resolution, was a crucial compromise to maintain real-time responsiveness while ensuring adequate note distinction.
As we look to the future, several enhancements are on the horizon.
We aim to improve the algorithm's accuracy in note detection, especially in distinguishing
closely spaced semitones. Another focus will be reducing latency further without compromising resolution,
potentially exploring machine learning techniques for more efficient signal processing. Additionally, expanding
compatibility to include various guitar types and incorporating user feedback to refine the gameplay experience are among our top priorities.
This project not only represents a significant step in interactive music gaming but also opens avenues for more advanced audio processing
applications on compact devices like the Raspberry Pi.
ml2743@cornell.edu
yc2733@cornell.edu
Real Guitar Hero_code
--source code
main.py
"""
ECE5725 Wesdnesday
Author: Yang Chen(yc2733) & Miaocheng li(ml2743)
Date: 2023/12/07
"""
from util import *
import RPi.GPIO as GPIO
import time
import os
import pygame
global SCORE
SCORE = 0
def GPIO17_callback(channel): # GPIO 17 quit button
print("quit button has been pushed !!!")
global_var.set_value('code_run', False)
global_var.set_value('game_run', False)
if __name__ == "__main__":
GPIO.setmode(GPIO.BCM)
global_var._init()
global_var.set_value('code_run', True)
global_var.set_value('game_run', False)
global_var.set_value('is_regame', False)
os.putenv('SDL_VIDEODRIVER', 'fbcon') # Display on piTFT
os.putenv('SDL_FBDEV', '/dev/fb0')
os.putenv('SDL_MOUSEDRV', 'TSLIB') # Track mouse clicks on piTFT
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(17, GPIO.FALLING, callback=GPIO17_callback, bouncetime=300)
# pygame.mouse.set_visible(False)
while global_var.get_value('code_run'):
if not global_var.get_value('is_regame'):
init.init()
if not global_var.get_value('code_run'):
break
file_name = choose.choose()
else:
global_var.set_value('is_regame', False)
if file_name == "menu":
continue
else:
global_var.set_value('game_run', True)
gen_time, arrive_time, det_start_time, det_end_time, out_time, stream_start_time, tab = data_process.load_tab(file_name)
# print(pause_after-pause_before)
sound = pygame.mixer.Sound("/home/pi/ece5725/project/guitar_hero/audio/60bpm_16_new.wav")
global_var.set_value('sound', sound)
pygame.mixer.init()
global_var.get_value('sound').play()
game.game_loop(gen_time, arrive_time, det_start_time, det_end_time, out_time, stream_start_time, tab)
settlement.game_over()
block_27_shutdown.py
import RPi.GPIO as GPIO
import os
import subprocess
if __name__ == "__main__":
GPIO.setmode(GPIO.BCM)
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.wait_for_edge(27, GPIO.FALLING)
subprocess.run("sudo shutdown -h now".split(" "))
util/_init_.py
__all__ = ['choose', 'data_process', 'init', 'refresh', 'global_var', 'game', 'detection','settlement' ,'pause']
util/choose.py
import pygame
from pygame.locals import * # for event MOUSE variables
import os
from util import global_var,init
# import RPi.GPIO as GPIO
# GPIO.setmode(GPIO.BCM)
WHITE = 255, 255, 255
BLACK = 0,0,0
pos = 0
x =0
y = 0
def choose():
pygame.init()
# pygame.mouse.set_visible(False)
screen = pygame.display.set_mode((320,240))
choose_font = pygame.font.Font(None,30)
choose_buttons = {'E_String':(160,60), 'C_major':(160,100),'Bent_to_fly':(160,140),'Back':(240,20)}
screen.fill(BLACK)
choose_buttons_rect = {}
for text, text_pos in choose_buttons.items():
text_surface = choose_font.render(text,True,WHITE)
rect = text_surface.get_rect(center=text_pos)
screen.blit(text_surface,rect)
choose_buttons_rect[text] = rect
pygame.display.flip()
while global_var.get_value('code_run'):
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
for(my_text, rect) in choose_buttons_rect.items():
if(rect.collidepoint(x,y)):
if(my_text == 'E_String'):
print("E_String pressed")
return my_text+".csv"
elif(my_text == 'Bent_to_fly'):
print("Bent_to_fly play")
return my_text+".csv"
elif(my_text == 'C_major'):
print("C_major play")
return my_text+".csv"
elif(my_text == 'Back'):
print('Back pressed')
return "menu"
# global_var.set_value('code_run',False)
util/data_process.py
import csv
import os
from util import global_var
def load_tab(file_name):
delta_arrive = 1.5
delta_start = 0.2
delta_end = 0.15
delta_out = 1.75
delta_stream_create = 0.015
# delta_start = 0.4
# delta_end = 0.3
# print(os.getcwd())
aug_file_name = "/home/pi/ece5725/project/guitar_hero/data/" + file_name
with open(aug_file_name, 'r') as csvfile:
reader = csv.reader(csvfile, delimiter=' ', quotechar='|')
next(reader) # skip header
time_slot = []
notes = []
for row in reader:
notes.append(row[0].split(','))
time_slot.append(notes[-1].pop(0))
notes[-1].reverse()
# print(', '.join(row))
if file_name == "Bent_to_fly.csv":
unit = 0.25
delta_arrive = 1
global_var.set_value("bpm",1)
elif file_name == "E_String.csv":
unit = 0.0625
delta_arrive = 1
global_var.set_value("bpm",1)
elif file_name == "C_major.csv":
unit = 0.25
delta_arrive = 1
global_var.set_value("bpm",1)
gen_time = [int(time)*unit for time in time_slot]
arrive_time = [time + delta_arrive for time in gen_time]
det_start_time = [time - delta_start for time in arrive_time]
det_end_time = [time + delta_end for time in arrive_time]
out_time = [time + delta_out for time in gen_time]
stream_start_time = [time - delta_stream_create for time in det_start_time]
stream_start_time[0] -= 0.1
return gen_time, arrive_time, det_start_time, det_end_time, out_time, stream_start_time, notes
util/detection.py
import numpy as np
from util import global_var
freq_strings = [[82.41,87.31,92.5,98,103.83,110,116.54,123.47,130.81,138.59,146.83,155.56,164.81,174.61,185,196,207.65,220,233.08,246.94,261.63,277.18], [110,116.54,123.47,130.81,138.59,146.83,155.56,164.81,174.61,185,196,207.65,220,233.08,246.94,261.63,277.18,293.66,311.13,329.63,349.23,369.99], [146.83,155.56,164.81,174.61,185,196,207.65,220,233.08,246.94,261.63,277.18,293.66,311.13,329.63,349.23,369.99,392,415.3,440,466.16,493.88], [196,207.65,220,233.08,246.94,261.63,277.18,293.66,311.13,329.63,349.23,369.99,392,415.3,440,466.16,493.88,523.25,554.37,587.33,622.25,659.25], [246.94,261.63,277.18,293.66,311.13,329.63,349.23,369.99,392,415.3,440,466.16,493.88,523.25,554.37,587.33,622.25,659.25,698.46,739.99,783.99,830.61], [329.63,349.23,369.99,392,415.3,440,466.16,493.88,523.25,554.37,587.33,622.25,659.25,698.46,739.99,783.99,830.61,880,932.33,987.77,1046.5,1108.73]]
sr = 44100
ts = 1.0/sr
rs = 44100/4096
def fft_detect(data, tab, timing):
ref = []
freq_domain = np.fft.fft(data)
freq_domain = np.abs(freq_domain[:300])
max_amp_idx = np.argsort(freq_domain)[-5:]
detected = False
for i in range(6):
if tab[i] != '-1':
ref.append(freq_strings[5-i][int(tab[i])])
if round(ref[-1]/rs) in max_amp_idx and round(ref[-1]*2/rs) in max_amp_idx:
global_var.set_right_notes(i, True, timing)
detected = True
if timing == 0:
global_var.set_value('score',global_var.get_value('score')+10)
elif timing == -1:
global_var.set_value('score',global_var.get_value('score')-3)
else:
global_var.set_value('score',global_var.get_value('score')-5)
return detected
util/game.py
from util import refresh, global_var, detection, pause
import pyaudio
import numpy as np
import time
import matplotlib.pyplot as plt
import wave
import pygame
import threading
form_1 = pyaudio.paInt16 # 16-bit resolution
chans = 1 # 1 channel
samp_rate = 44100 # 44.1kHz sampling rate
# dev_index = 1
window_size = 4096
d_window_size = 1024
def create_stream():
id = global_var.get_value('stream_id')
if id < 4:
start_create = time.time()
stream = global_var.get_value('pyaudio').open(format = form_1,rate = samp_rate,channels = chans, \
input = True, \
frames_per_buffer=d_window_size)
print("{} takes to create stream".format(time.time()-start_create))
global_var.set_value('audio_stream'+str(id), stream)
else:
stream = global_var.get_value('audio_stream'+str(id%4))
stream.start_stream()
stream.read(stream.get_read_available())
global_var.set_value('stream_id', id+1)
def game_loop(gen_time, arrive_time, det_start_time, det_end_time, out_time, stream_start_time, tab):
audio = pyaudio.PyAudio() # Create pyaudio instantiation
global_var.set_value('pyaudio', audio)
sampling_16_width = audio.get_sample_size(pyaudio.paInt16)
global_var.set_value('stream_id', 0);
global_var.set_value('sound_start',time.time())
pause.back_game()
Ref = refresh.Refresher(gen_time, arrive_time, det_start_time, det_end_time, out_time, tab)
stream_start_time.append(stream_start_time[-1]+5)
concern_list = []
idx = -1
start_time = time.time()
# right_note = False
global_var.set_value('score',0)
while(global_var.get_value("game_run")):
if global_var.get_value("is_resume"):
print("resume!")
Ref.refresh()
start_time += time.time() - global_var.get_value("pause_time")
now = time.time() - start_time
print(now)
now = time.time() - start_time
Ref.refresh()
if (global_var.get_value("is_resume")):
continue
if len(concern_list) == 0:
if stream_start_time[idx+1] < now:
idx += 1
concern_list.append(idx)
audio_stream_thread = threading.Thread(target=create_stream)
audio_stream_thread.start()
if idx == 16:
print()
detected = False
is_first_window = True
if len(concern_list) and now < det_end_time[idx]:
if is_first_window and now - det_start_time[idx] > 0.093 and global_var.get_value('audio_stream'+str(idx%4)) is not None:
time_1 = time.time()
chunk = global_var.get_value('audio_stream'+str(idx%4)).read(window_size)
print("{} takes to wait for 1 buffer filled".format(time.time() - time_1))
data = np.fromstring(chunk,dtype=np.int16)
is_first_window = False
window_end_time = now
this_detected = detection.fft_detect(data, tab[idx], -1)
detected = detected if detected else this_detected
det_cnt = 0
elif not detected and not is_first_window and now - window_end_time > d_window_size/45000:
det_cnt += 1
print("{} note, dete_cnt = {}".format(idx, det_cnt))
try:
appendix = global_var.get_value('audio_stream'+str(idx%4)).read(d_window_size)
chunk = chunk[2*d_window_size:] + appendix
data = np.fromstring(chunk,dtype=np.int16)
timing = 0 if det_cnt < 7 else 1
this_detected = detection.fft_detect(data, tab[idx], timing)
detected = detected if detected else this_detected
window_end_time = now
except:
print("stream closed!")
elif len(concern_list) and now > det_end_time[idx]:
pop_idx = concern_list.pop(0)
global_var.get_value('audio_stream'+str(pop_idx%4)).stop_stream()
if detected == False:
global_var.set_value('score',global_var.get_value('score')-10)
global_var.set_miss(pop_idx)
util/global_var.py
import threading
import time
def _init(): # initialize
global _global_dict, _right_notes, score, _miss_idx
_global_dict = {}
_right_notes = [[False,8], [False,8], [False,8], [False,8], [False,8], [False,8]]
_miss_idx = []
score = 0
def set_value(key, value):
# create / set value
_global_dict[key] = value
def get_value(key):
try:
return _global_dict[key]
except:
print('Global Var not found.')
def set_right_notes(string_id, right, timing):
if timing < _right_notes[string_id][1]:
_right_notes[string_id] = [right, timing]
def clean_right_notes(string_id):
_right_notes[string_id] = [False, 8]
def get_right_notes():
right_notes = []
for i, note in enumerate(_right_notes):
if note[0]:
right_notes.append([i,note[1]])
return right_notes
def miss_set_alarm():
time.sleep(0.4)
_miss_idx.pop(0)
def set_miss(idx):
_miss_idx.append(idx)
thread = threading.Thread(target=miss_set_alarm)
thread.start()
def get_miss_notes():
return _miss_idx
util/init.py
import pygame
from pygame.locals import * # for event MOUSE variables
import os
from util import global_var
WHITE = 255, 255, 255
BLACK = 0,0,0
pos = 0
x =0
y = 0
def init():
pygame.init()
pygame.mouse.set_visible(False)
screen = pygame.display.set_mode((320, 240))
cover_font = pygame.font.Font(None, 40)
cover_buttons = {'Start':(80,180), 'Quit':(240,180)}
screen.fill(BLACK) # Erase the Work space
cover_buttons_rect = {} # Create a rect dictionary
background_image = pygame.image.load('/home/pi/ece5725/project/guitar_hero/image/cover.png').convert()
screen.blit(background_image, [0, 0])
for text, text_pos in cover_buttons.items():
text_surface = cover_font.render(text, True,BLACK)
rect = text_surface.get_rect(center=text_pos)
screen.blit(text_surface,rect)
cover_buttons_rect[text] = rect
pygame.display.flip()
while global_var.get_value('code_run'):
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
for ( my_text, rect) in cover_buttons_rect.items(): # for saved button rects....
if ( rect.collidepoint (x,y) ): # if collide with mouse click....
if (my_text == 'Start'): # indicate correct button press
print ("Start pressed")
return
# Start_pressed = True
if (my_text == 'Quit'):
print ("Quit pressed")
global_var.set_value('code_run',False)
util/pause.py
import pygame
from pygame.locals import * # for event MOUSE variables
import os
from util import global_var,init
import time
# import RPi.GPIO as GPIO
# GPIO.setmode(GPIO.BCM)
WHITE = 255, 255, 255
BLACK = 0,0,0
pos = 0
x =0
y = 0
def back_game():
screen = pygame.display.set_mode((320,240))
num_font = pygame.font.Font(None,80)
num_tab = {'Three':(160,120),'Two':(160,120),'One':(160,120)}
screen.fill(BLACK)
num_rect = {}
time_unit = global_var.get_value("bpm")
for num,num_pos in num_tab.items():
num_surface = num_font.render(num,True,WHITE)
rect = num_surface.get_rect(center=num_pos)
screen.blit(num_surface,rect)
num_rect[num] = rect
pygame.display.flip()
time.sleep(time_unit)
screen.fill(BLACK)
return
# return to the game(TBD)
def pause():
pygame.init()
# pygame.mouse.set_visible(False)
global_var.get_value('sound').stop()
screen = pygame.display.set_mode((320,240))
pause_font = pygame.font.Font(None,30)
pause_buttons = {'Resume':(160,120), 'Quit':(160,180)}
screen.fill(BLACK)
pause_buttons_rect = {}
for text, text_pos in pause_buttons.items():
text_surface = pause_font.render(text,True,WHITE)
rect = text_surface.get_rect(center=text_pos)
screen.blit(text_surface,rect)
pause_buttons_rect[text] = rect
pygame.display.flip()
while global_var.get_value('code_run'):
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
for(pause_text,rect) in pause_buttons_rect.items():
if(rect.collidepoint(x,y)):
if(pause_text == 'Resume'):
print("game pause")
return 'back_game'
elif(pause_text == 'Quit'):
global_var.get_value('sound').stop()
print ("Quit pressed, back to the menu")
# back to the menu
return 'quit'
util/refresh.py
import time
import pygame
from pygame.locals import * # for event MOUSE variables
from util import global_var,pause,init
import threading
import os
WHITE = 255, 255, 255
BLACK = 0,0,0
RED = 255, 0, 0
pos = 0
class Refresher:
def __init__(self,gen_time, arrive_time, det_start_time, det_end_time, out_time, tab):
self.gen_time = gen_time
self.arrive_time = arrive_time
self.det_start_time = det_start_time
self.det_end_time = det_end_time
self.out_time = out_time
self.tab = tab
self.arrive_delta = self.arrive_time[0] - self.gen_time[0]
self.screen = pygame.display.set_mode((320, 240))
self.pause_font = pygame.font.Font(None,30)
self.note_font = pygame.font.Font(None, 30)
self.birth_point = 20
self.estring_height = 50
self.string_interval = 30
self.hit_box = 200
self.concern_list = [0]
self.start_time = time.time()
self.pause_time = 0
self.refresh_cycle_start = 0
self.milestone = 1
global_var.set_value('is_resume',False)
def refresh(self):
self.refresh_cycle_start = time.time()
if global_var.get_value('is_resume'):
self.start_time += time.time() - self.pause_time
now = time.time() - self.start_time
for i in self.concern_list:
color = WHITE if self.det_start_time[i] > now else RED
notes = []
for j, fret in enumerate(self.tab[i]):
if fret != '-1':
notes.append([fret,(self.birth_point + (now-self.gen_time[i]) / self.arrive_delta * (self.hit_box-self.birth_point),j*self.string_interval + self.estring_height)])
text_surface = self.note_font.render(fret, True, color)
rect = text_surface.get_rect(center=notes[-1][1])
self.screen.blit(text_surface,rect)
for i in range(6):
pygame.draw.line(self.screen, WHITE, (0,self.estring_height+i*self.string_interval), (320, self.estring_height+i*self.string_interval))
pygame.draw.line(self.screen, WHITE, (self.hit_box,0), (self.hit_box,240))
right_notes = global_var.get_right_notes()
miss_idx = global_var.get_miss_notes()
for notes in right_notes:
text = "perfect!" if notes[1] == 0 else "too early!" if notes[1] == -1 else "too late!"
text_surface = self.note_font.render(text, True, WHITE)
rect = text_surface.get_rect(center=(260,notes[0]*self.string_interval + self.estring_height))
self.screen.blit(text_surface, rect)
for idx in miss_idx:
for j, fret in enumerate(self.tab[idx]):
if fret != '-1':
text = "miss!"
text_surface = self.note_font.render(text, True, WHITE)
rect = text_surface.get_rect(center=(260,j*self.string_interval + self.estring_height))
self.screen.blit(text_surface, rect)
pygame.display.flip()
delta = (self.pause_time - self.start_time + 3 * global_var.get_value('bpm')) % (4 * global_var.get_value('bpm'))
global_var.get_value('sound').play()
time.sleep(delta)
self.start_time += delta
global_var.set_value('is_resume',False)
now = time.time() - self.start_time
if now - self.arrive_time[-1] > 3:
global_var.set_value("game_run", False)
return
self.screen.fill(BLACK)
try:
i = self.concern_list[-1] + 1
except:
return
notes = []
while i < len(self.gen_time):
if self.gen_time[i] < now:
self.concern_list.append(i)
i += 1
else:
break
while len(self.concern_list) and self.out_time[self.concern_list[0]] < now:
i = self.concern_list.pop(0)
for j, fret in enumerate(self.tab[i]):
if fret != '-1':
global_var.clean_right_notes(j)
for i in self.concern_list:
color = WHITE if self.det_start_time[i] > now else RED
for j, fret in enumerate(self.tab[i]):
if fret != '-1':
notes.append([fret,(self.birth_point + (now-self.gen_time[i]) / self.arrive_delta * (self.hit_box-self.birth_point),j*self.string_interval + self.estring_height)])
text_surface = self.note_font.render(fret, True, color)
rect = text_surface.get_rect(center=notes[-1][1])
self.screen.blit(text_surface,rect)
for i in range(6):
pygame.draw.line(self.screen, WHITE, (0,self.estring_height+i*self.string_interval), (320, self.estring_height+i*self.string_interval))
pygame.draw.line(self.screen, WHITE, (self.hit_box,0), (self.hit_box,240))
right_notes = global_var.get_right_notes()
miss_idx = global_var.get_miss_notes()
for notes in right_notes:
text = "perfect!" if notes[1] == 0 else "too early!" if notes[1] == -1 else "too late!"
text_surface = self.note_font.render(text, True, WHITE)
rect = text_surface.get_rect(center=(260,notes[0]*self.string_interval + self.estring_height))
self.screen.blit(text_surface, rect)
for idx in miss_idx:
for j, fret in enumerate(self.tab[idx]):
if fret != '-1':
text = "miss!"
text_surface = self.note_font.render(text, True, WHITE)
rect = text_surface.get_rect(center=(260,j*self.string_interval + self.estring_height))
self.screen.blit(text_surface, rect)
score_text_surface = self.note_font.render('score: {}'.format(global_var.get_value('score')), True, WHITE)
pause_text_surface = self.pause_font.render('Pause',True,WHITE)
pause_rect = pause_text_surface.get_rect(center=(60,20))
score_rect = score_text_surface.get_rect(center=(260,20))
self.screen.blit(score_text_surface, score_rect)
self.screen.blit(pause_text_surface, pause_rect)
pygame.display.flip()
x, y = -1, -1
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
if(pause_rect.collidepoint(x,y)):
print("pause pressed")
self.pause_time = time.time()
commond = pause.pause()
if commond == 'back_game':
pause.back_game()
global_var.set_value('is_resume',True)
global_var.set_value("pause_time", self.pause_time)
global_var.set_value("pause_time_delta", (self.pause_time - self.start_time + 3 * global_var.get_value('bpm')) % (4 * global_var.get_value('bpm')))
# back to the game!!!!! HERE!!!!!
elif commond == 'quit':
global_var.set_value('game_run',False)
util/settlement.py
import pygame
from pygame.locals import * # for event MOUSE variables
import os
from util import global_var
# import RPi.GPIO as GPIO
# GPIO.setmode(GPIO.BCM)
WHITE = 255, 255, 255
BLACK = 0,0,0
pos = 0
x =0
y = 0
def game_over():
global_var.get_value('sound').stop()
if global_var.get_value('score') < 0:
game_over_sound = pygame.mixer.Sound("/home/pi/ece5725/project/guitar_hero/audio/fail_game_over_16.wav")
else:
game_over_sound = pygame.mixer.Sound("/home/pi/ece5725/project/guitar_hero/audio/game_over_16.wav")
game_over_sound.play()
pygame.init()
# SCORE = global_var.get_value('score')
print(global_var.get_value('score'))
SCORE_label = 'your score is ' + str(global_var.get_value('score'))
# pygame.mouse.set_visible(False)
screen = pygame.display.set_mode((320,240))
final_font = pygame.font.Font(None,30)
final_buttons = {SCORE_label:(160,100),'REGAME':(80,140),'Quit':(240,140)}
screen.fill(BLACK)
final_buttons_rect = {}
for text, text_pos in final_buttons.items():
text_surface = final_font.render(text,True,WHITE)
rect = text_surface.get_rect(center=text_pos)
screen.blit(text_surface,rect)
final_buttons_rect[text] = rect
pygame.display.flip()
global_var.set_value('pause_run',True)
while global_var.get_value('pause_run'):
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
pos = pygame.mouse.get_pos()
x,y = pos
for(my_text, rect) in final_buttons_rect.items():
if(rect.collidepoint(x,y)):
if(my_text == 'REGAME'):
game_over_sound.stop()
print("regame")
global_var.set_value('score',0)
global_var.set_value('is_regame', True)
global_var.set_value('pause_run', False)
elif(my_text == 'Quit'):
game_over_sound.stop()
global_var.set_value('score',0)
print("game over")
global_var.set_value('pause_run',False)